﻿using System;
using System.Collections.Generic;
using System.Windows.Threading;
using IronCow;
using System.Windows.Input;
using System.Windows.Data;

namespace Milkify.Data
{
    [System.Diagnostics.DebuggerDisplay("TaskListListViewData [{TaskList.Count}]")]
    public class TaskListListViewData
    {
        private class RefreshAsyncState
        {
            public Rtm Rtm { get; private set; }
            public Func<Rtm, IEnumerable<TaskList>> Func { get; private set; }

            public RefreshAsyncState(Rtm rtm, Func<Rtm, IEnumerable<TaskList>> func)
            {
                Rtm = rtm;
                Func = func;
            }
        }

        private bool mHasDeliberatelyNoTaskLists = false;
        public TaskListViewDataCollection TaskLists { get; private set; }

        public ICommand NavigateToTaskListCommand { get; set; }

        public string Title { get; set; }

        private bool mShowArchivedLists = false;
        public bool ShowArchivedLists
        {
            get { return mShowArchivedLists; }
            set
            {
                mShowArchivedLists = value;
                FilterArchivedLists(TaskLists, !value);
            }
        }

        public TaskListListViewData()
        {
            Title = "Lists";
            TaskLists = new TaskListViewDataCollection();
        }

        public TaskListListViewData(IEnumerable<TaskListViewData> taskLists)
            : this()
        {
            foreach (var taskList in taskLists)
            {
                TaskLists.Add(taskList);
            }

            mHasDeliberatelyNoTaskLists = (TaskLists.Count == 0);
        }

        public void RefreshAsync(Rtm rtm)
        {
            if (TaskLists.Count == 0 && !mHasDeliberatelyNoTaskLists)
            {
                var func = new Func<Rtm, IEnumerable<TaskList>>(GetRequestedTaskLists);
                var asyncState = new RefreshAsyncState(rtm, func);
                func.BeginInvoke(rtm, RefreshAsyncCallback, asyncState);
            }
            else
            {
                foreach (var taskList in TaskLists)
                {
                    taskList.RefreshAsync(rtm);
                }
            }
        }

        private void RefreshAsyncCallback(IAsyncResult ar)
        {
            try
            {
                RefreshAsyncState asyncState = (RefreshAsyncState)ar.AsyncState;
                var rtm = asyncState.Rtm;
                var func = asyncState.Func;
                IEnumerable<TaskList> taskLists = func.EndInvoke(ar);

                if (App.Current == null || App.Current.Dispatcher.CheckAccess())
                {
                    SetTaskListsAndRefreshAsync(taskLists, rtm);
                }
                else
                {
                    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action<IEnumerable<TaskList>, Rtm>)SetTaskListsAndRefreshAsync, taskLists, rtm);
                }
            }
            catch (Exception e)
            {
                // Raise this exception on the UI thread so it's picked up
                // by the application exception handler.
                App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => { throw e; }));
            }
        }

        private IEnumerable<TaskList> GetRequestedTaskLists(Rtm rtm)
        {
            return rtm.TaskLists;
        }

        private void SetTaskLists(IEnumerable<TaskList> taskLists)
        {
            lock (this)
            {
                TaskLists.Clear();
                if (taskLists != null)
                {
                    foreach (var taskList in taskLists)
                    {
                        var taskListData = new TaskListViewData(taskList);
                        TaskLists.Add(taskListData);
                    }
                }
            }
        }

        private void SetTaskListsAndRefreshAsync(IEnumerable<TaskList> taskLists, Rtm rtm)
        {
            SetTaskLists(taskLists);
            FilterArchivedLists(TaskLists, !ShowArchivedLists);
            foreach (var taskListData in TaskLists)
            {
                taskListData.RefreshAsync(rtm);
            }
        }

        private void FilterArchivedLists(IEnumerable<TaskListViewData> taskLists, bool yesNo)
        {
            var collectionView = CollectionViewSource.GetDefaultView(TaskLists);
            if (collectionView != null)
            {
                if (yesNo)
                {
                    collectionView.Filter = o =>
                        {
                            TaskListViewData list = (TaskListViewData)o;
                            if (list.TaskList == null)
                                return true;
                            return (!list.TaskList.GetFlag(TaskListFlags.Archived));
                        };
                }
                else
                {
                    collectionView.Filter = null;
                }
            }
        }
    }
}
